/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ga.coordination;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
import java.util.Set;

/**
 *
 * @author Acer
 */
public class GACoordinator {
    //Panjang string chromosome
    public byte chromosomeLength;
    
    //index maximum yang dipakai
    byte M;
    
    //machine List untuk fitness function
    public Machine []machineList;
    
    //Capacity adalah kapasitas truck
    public byte capacity;
    
    //pathList digunakan untuk pathFindingLayer
    public byte [][][] pathList;
    
    //Factory digunakan untuk pathFindingLayer
    public Factory factory;
    
    public HashMap<Character,Vendor> vendorList ;
    
    //Ukuran populasi
    public byte populationSize = 20;
    
    //populasi
    byte [][] population = new byte[populationSize][];
    //populasi sementara
    byte [][] tempPopulation = new byte[populationSize][];
    
    //array of string, setiap string berisi path dengan source/dest berupa 'A','B',...'Z'
    StringBuffer [] arrayTruckPath = new StringBuffer[populationSize];
    //truckPath sementara
    StringBuffer [] tempArrayTruckPath = new StringBuffer[populationSize];
    
    //fitness value dari setiap individu
    long []fitnessValue = new long[populationSize];
    //fitness value sementara
    long []fitnessValueTemp  = new long[populationSize];
    
    //individu untuk temp
    byte []individu;
    byte []individu2;
    
    //selection value, a thresold for steady-state selection
    byte thresold=1;
    
    //persentase untuk crossover,bagusnya 75-80, max 100
    byte crossOverProbability=80;
    
    //persentase untuk mutation, max 10000
    int mutationProbability=1000;
    
    //Elitism
    byte [] elitismString;
    long fitnessElitism;
    StringBuffer pathTruckElitism;
    ArrayList<Byte> compactIndividuElitism = new ArrayList<Byte>();
    
    //String Buffer untuk LOG
    StringBuffer LogForGA = new StringBuffer();
    
    //Array List untuk GUI(hanya 4 saja)
    ArrayList<ArrayList<Byte>> AABLog;
    ArrayList<Byte> ABLog;
    
    public void PerformElitism(){
        System.out.print("Elitism String : ");
        this.PerformArr(elitismString);
        System.out.println("Fitness Elitism : "+ fitnessElitism);
    }
    
    public void PerformAllFitness(){
        int i;
        PerformElitism();
        for(i=0;i<fitnessValue.length;++i){
            System.out.print(i + " : " + fitnessValue[i] + " ,string : ");
            this.PerformArr(population[i]);
        }
    }
    
    //For random machine
    Random randomGA = new Random();
    
    /**
     * Constructor GACoordinator
     * @param H digunakan untuk panjang kromosom
     * @param M digunakan untuk banyak jenis mesin maximum yang diizinkan
     * @param machineList merupakan list mesin
     * @param capacity  untuk Kapasitas truck
     */
    public GACoordinator(byte H,byte M,Machine []machineList,byte capacity,byte [][][] pathList,Factory factory, HashMap<Character,Vendor> vendorList) {
        chromosomeLength = (byte) (H-1);
        this.M = M;
        this.machineList = machineList;
        this.capacity = capacity;
        this.pathList = pathList;
        this.factory = factory;
        this.vendorList = vendorList;
        int i;
        
        //Inisialisasi individu dan individu2
        individu = new byte[chromosomeLength];
        for(i=0;i<individu.length;++i){
            individu[i]=1;
        }
        
        individu2 = new byte[chromosomeLength];
        for(i=0;i<individu2.length;++i){
            individu2[i]=1;
        }
        
        //Inisialisasi elitism
        elitismString = new byte[chromosomeLength];
        for(i=0;i<elitismString.length;++i){
            elitismString[i]=1;
        }
        fitnessElitism = 0;
        
        //Inisialisasi populasi
        for(i=0;i<populationSize;++i){
            population[i] = new byte[chromosomeLength];
            tempPopulation[i] = new byte[chromosomeLength];
        }
        
        //Inisialisasi truckPath
        for(i=0;i<arrayTruckPath.length;++i){
            arrayTruckPath[i] = new StringBuffer("");
        }
        
        //Inisialisasi array 2 dimensi untuk AAB
        AABLog = new ArrayList<ArrayList<Byte>>(4);
        ABLog = new ArrayList<Byte>();
    }
    
    /**
     * Generate populate
     */
    public void GeneratePopulate(){
        //First generated all random, standard procedure
        int i,j;
        for(i=0;i<populationSize;++i){
            for(j=0;j<chromosomeLength;++j){
                population[i][j] = (byte)(randomGA.nextInt(this.M));
                tempPopulation[i][j] = population[i][j];
            }
        }
        
        //Generating for testing      
        
        //DEBUG tampilkan
//        this.PerformArr2D(population);
        
        
    }
    
    
    //array of boolean for compaction layer
    boolean [] isEverInsertedToCompactIndividu = new boolean[30];
    
    //array list untuk menyimpan individu yang dicompact
    ArrayList<Byte> compactIndividu = new ArrayList<Byte>();
    
    //array list sementara untuk pengganti compact individu
    
    /**
     * Return the minimum time from compactIndividu
     */
    public StringBuffer countMinimumTimeOddEven(){
        int i,j,k;
        
        //Kasus untuk odd path digunakan
        int timeForOdd=0,minTime=2501;
        char placeMinTime1='P',placeMinTime2='P';
        StringBuffer retvalOdd = new StringBuffer("P");
        i=0;
        //Hitung untuk posisi awal
        for(j=0;j<machineList[compactIndividu.get(i)].idVendor.size();++j){
            if (minTime>this.pathList[(int)'P'][(int)machineList[compactIndividu.get(i)].idVendor.get(j)].length){
                minTime = this.pathList[(int)'P'][(int)machineList[compactIndividu.get(i)].idVendor.get(j)].length;
                placeMinTime2 = machineList[compactIndividu.get(i)].idVendor.get(j);
            }
        }
        
        
        //Tambahkan ke string solusi
        timeForOdd += this.pathList[(int)'P'][(int)placeMinTime2].length;
        retvalOdd.append(placeMinTime2);
        i+=2;
        
        
//        System.out.println("TOTAL GANJIL :" + timeForOdd);
//        System.out.println(retvalOdd);
            
        //Hitung untuk posisi posisi 
        while (i<compactIndividu.size()){
            minTime = 2501;
            for(j=0;j<this.machineList[compactIndividu.get(i-1)].idVendor.size();++j){
                for(k=0;k<this.machineList[compactIndividu.get(i)].idVendor.size();++k){
                    if (minTime>this.pathList[(int)machineList[compactIndividu.get(i-1)].idVendor.get(j)][(int)machineList[compactIndividu.get(i)].idVendor.get(k)].length){
                        minTime=this.pathList[(int)machineList[compactIndividu.get(i-1)].idVendor.get(j)][(int)machineList[compactIndividu.get(i)].idVendor.get(k)].length;
                        placeMinTime1 = machineList[compactIndividu.get(i-1)].idVendor.get(j);
                        placeMinTime2 = machineList[compactIndividu.get(i)].idVendor.get(k);
                    }
                }
            }
            //Tambahkan ke string solusi
            timeForOdd += this.pathList[(int)placeMinTime1][(int)placeMinTime2].length;
            timeForOdd += this.pathList[retvalOdd.charAt(retvalOdd.length()-1)][placeMinTime1].length;
            retvalOdd.append(placeMinTime1);
            retvalOdd.append(placeMinTime2);
            i+=2;
            
//            System.out.println("TOTAL GANJIL :" + timeForOdd);
//            System.out.println(retvalOdd);
            
        }
        
        //Check posisi akhir untuk versi ganjil
        if (i==compactIndividu.size()){
            minTime = 2501;
            for(j=0;j<machineList[compactIndividu.get(i-1)].idVendor.size();++j){
                if (minTime>this.pathList[(int)machineList[compactIndividu.get(i-1)].idVendor.get(j)][(int)'P'].length){
                    minTime=this.pathList[(int)machineList[compactIndividu.get(i-1)].idVendor.get(j)][(int)'P'].length;
                    placeMinTime1 = machineList[compactIndividu.get(i-1)].idVendor.get(j);
                }
            }
            //Tambahkan ke string solusi
            timeForOdd += minTime;
            timeForOdd += this.pathList[retvalOdd.charAt(retvalOdd.length()-1)][placeMinTime1].length;
            retvalOdd.append(placeMinTime1);
            retvalOdd.append('P');
        }else{
            timeForOdd += this.pathList[retvalOdd.charAt(retvalOdd.length()-1)]['P'].length;
            retvalOdd.append('P');
        }
        
        //DEBUG
//        System.out.println("TOTAL GANJIL :" + timeForOdd);
//        System.out.println(retvalOdd);
        
        //Kasus untuk even path digunakan
        int timeForEven=0;
        
        minTime=0;
        placeMinTime1='P';placeMinTime2='P';
        StringBuffer retvalEven = new StringBuffer("P");
        
        i=1;
        //Hitung untuk posisi posisi 
        while (i<compactIndividu.size()){
            minTime = 2501;
            for(j=0;j<this.machineList[compactIndividu.get(i-1)].idVendor.size();++j){
                for(k=0;k<this.machineList[compactIndividu.get(i)].idVendor.size();++k){
                    if (minTime>this.pathList[(int)machineList[compactIndividu.get(i-1)].idVendor.get(j)][(int)machineList[compactIndividu.get(i)].idVendor.get(k)].length){
                        minTime=this.pathList[(int)machineList[compactIndividu.get(i-1)].idVendor.get(j)][(int)machineList[compactIndividu.get(i)].idVendor.get(k)].length;
                        placeMinTime1 = machineList[compactIndividu.get(i-1)].idVendor.get(j);
                        placeMinTime2 = machineList[compactIndividu.get(i)].idVendor.get(k);
                    }
                }
            }
            //Tambahkan ke string solusi
            timeForEven += this.pathList[placeMinTime1][placeMinTime2].length;
            timeForEven += this.pathList[retvalEven.charAt(retvalEven.length()-1)][placeMinTime1].length;
            
            retvalEven.append(placeMinTime1);
            retvalEven.append(placeMinTime2);
            i+=2;
            
            
            
//            System.out.println("TOTAL GENAP :" + timeForEven);
//            System.out.println(retvalEven);
        }
        
        //Check posisi akhir untuk versi genap
        if (i==compactIndividu.size()){
            minTime = 2501;
            for(j=0;j<machineList[compactIndividu.get(i-1)].idVendor.size();++j){
                if (minTime>this.pathList[(int)machineList[compactIndividu.get(i-1)].idVendor.get(j)][(int)'P'].length){
                    minTime=this.pathList[(int)machineList[compactIndividu.get(i-1)].idVendor.get(j)][(int)'P'].length;
                    placeMinTime1 = machineList[compactIndividu.get(i-1)].idVendor.get(j);
                }
            }
            //Tambahkan ke string solusi
            timeForEven += minTime;
            timeForEven += this.pathList[retvalEven.charAt(retvalEven.length()-1)][placeMinTime1].length;
            retvalEven.append(placeMinTime1);
            retvalEven.append('P');
        }else{
            timeForEven += this.pathList[retvalEven.charAt(retvalEven.length()-1)]['P'].length;
            retvalEven.append('P');
        }
        
        
//        System.out.println("TOTAL GENAP :" + timeForEven);
//        System.out.println(retvalEven);
            
        if (timeForOdd < timeForEven){
            return retvalOdd;
        }
        return retvalEven;
    }
    
    /**
     * Mencari pathFinding mesin terpendek 
     * aslongas 1 jam masih bisa dilaksanakan,capacity truck masih muat,(SATU PP)
     * fund masih ada untuk sebuah mesin, hour factory masih ada,
     * nRentMachine masih bisa,
     * list mesin dari compaction masih ada
     */
    public StringBuffer pathFindingLayer(StringBuffer retvalBef){
        int i,j;
        
        //retval selalu dimulai dari pabrik
        StringBuffer retvalNext = new StringBuffer("P");
        //temp untuk compact individu
        ArrayList<Byte> tempCompactIndividu = new ArrayList<Byte>();
        
        char source='P',destination='P';
        ArrayList<Byte> listOfMachine = new ArrayList<Byte>();
        //temporary constraint variable, used only for PP
        int minutesUsed = 0,nextMinutesUsed=0;
        byte capacityUsed = 0,nextCapacityUsed=0;
        
        long fundUsed = 0;//DIGUNAKAN DI AKHIR FUNGSI
        int hourFactoryUsed = 0;//Dalam menit saja, lebih presisi
        byte nRentMachineUsed = 0;
        
        //index source mesin dari sudut pandang retvalBef
//        int idxMachineFirst = 0;
        int idxMachineLast = 1;
        
        source = retvalBef.charAt(0);
        destination = retvalBef.charAt(1);
        
        boolean isStillTrue,isTimeSufficient,isCapacitySufficient,isHourSufficient=true;
        
        while (idxMachineLast<retvalBef.length()){
            //Sewa satu mesin setiap loop, nilai mutlak tidak bisa direset
            
            
            isStillTrue = true;
            
//            isStillTrue = (fundUsed+this.machineList[compactIndividu.get(idxMachineLast-1)]<=this.factory.fund) && (hourFactoryUsed<this.factory.hour) && (nRentMachineUsed+1<=this.factory.nRentMachine);
            if (destination!='P'){
                if (fundUsed + this.machineList[compactIndividu.get(idxMachineLast-1)].rentCost > this.factory.fund){
                    isStillTrue = false;
                }
            }
            
//            System.out.println("TEST");
            if (hourFactoryUsed + (this.pathList[source][destination].length + this.pathList[destination]['P'].length) >  (int)(this.factory.hour)*60){
                isStillTrue = false;
                isHourSufficient = false;
                break;
            }
//            System.out.println("TEST");
            if (nRentMachineUsed + 1 > this.factory.totalMachine){
                isStillTrue = false;
                break;
            }
//            System.out.println("TEST");
            if (isStillTrue){
                
                if (destination=='P'){
//                    System.out.println("FIREEE");
                    //Kembali ke P
                    hourFactoryUsed += this.pathList[source]['P'].length;

                    //RESET nilai constraint temporary
                    minutesUsed=0;
                    capacityUsed=0;

                    //Isi ke compactIndividu, compactIndividu tidak diisi
                    //retvalNext diisi
                    retvalNext.append('P');

                    //Source jadi P,destination tetap
                    source= 'P';
                    //idxMachineLast tidak bertambah

                    //Isi listOfMachine ke tempCompactIndividu
                    while (!listOfMachine.isEmpty()){
                        tempCompactIndividu.add(listOfMachine.get(0));
                        listOfMachine.remove(0);
                    }
                    break;
                }else if ((idxMachineLast==retvalBef.length()-1) && destination!='P'){
//                    System.out.println("FIRE");
                }
//                System.out.println("TEST");
                //Mesin masih bisa digunakan
                //Check menit yang digunakan apakah masih dalam waktu 1 jam
                nextMinutesUsed = minutesUsed + this.pathList[source][destination].length + this.pathList[destination]['P'].length;
                if (nextMinutesUsed<= 60){
                    isTimeSufficient = true;
                }else{
                    isTimeSufficient = false;
                }
                
                //Check kapasitas truk, apakah masih bisa
                nextCapacityUsed = (byte) (capacityUsed + 1);
                if (nextCapacityUsed<= this.capacity){
                    isCapacitySufficient = true;
                }else{
                    isCapacitySufficient = false;
                }
                
                
                if (isTimeSufficient && isCapacitySufficient){
//                    System.out.println("BEFORE : " +idxMachineLast);
//                    System.out.println("s,c : " + source + "," +destination);
                    //Kasus jika waktu dan kapasitas truk bisa,ambil mesin
                    fundUsed+= this.machineList[compactIndividu.get(idxMachineLast-1)].rentCost;
                    hourFactoryUsed += (this.pathList[source][destination].length);
//                    hourFactoryUsed += (this.pathList[source][destination].length + this.pathList[destination]['P'].length);
                    nRentMachineUsed++;
                    
                    minutesUsed=this.pathList[source][destination].length;
                    capacityUsed=nextCapacityUsed;
                    
                    //Isi ke compactIndividu
                    listOfMachine.add(compactIndividu.get(idxMachineLast-1));
//                    tempCompactIndividu.add(compactIndividu.get(idxMachineLast-1));
                    
                    //Isi ke retvalNext, isi ulang source dan destination
                    retvalNext.append(destination);
                    source = destination;
                    idxMachineLast++;
                    destination = retvalBef.charAt(idxMachineLast);
                    
                    //DEBUG
//                    System.out.println("CASE 1");
//                    System.out.println("idx : " +idxMachineLast);
//                    System.out.println("DEBUG : " + retvalNext);
//                    System.out.println("fund : " + fundUsed);
//                    System.out.println("hour : " + hourFactoryUsed);
//                    System.out.println("rent : " + nRentMachineUsed);
//                    System.out.println("timetravel : "+ minutesUsed);
//                    System.out.println("capacity : " + capacityUsed);
                }else if (!isTimeSufficient && isCapacitySufficient){
                    //Waktu tidak cukup tapi kapasitas cukup, truk kembali
                    
                    if (capacityUsed>0){
                        //Kembali ke P
                        hourFactoryUsed += this.pathList[source]['P'].length;
                        
                        //RESET nilai constraint temporary
                        minutesUsed=0;
                        capacityUsed=0;
                        
                        //Isi ke compactIndividu, compactIndividu tidak diisi
                        //retvalNext diisi
                        retvalNext.append('P');
                        
                        //Source jadi P,destination tetap
                        source= 'P';
                        //idxMachineLast tidak bertambah
                        
                        //Isi listOfMachine ke tempCompactIndividu
                        while (!listOfMachine.isEmpty()){
                            tempCompactIndividu.add(listOfMachine.get(0));
                            listOfMachine.remove(0);
                        }
                        
                    }else{
                        //ambil barang, karena kapasitas masih kosong
                        fundUsed+= this.machineList[compactIndividu.get(idxMachineLast-1)].rentCost;
                        hourFactoryUsed += (this.pathList[source][destination].length + this.pathList[destination]['P'].length);
                        nRentMachineUsed++;

                        minutesUsed=nextMinutesUsed;
                        capacityUsed=nextCapacityUsed;

                        //Isi ke compactIndividu
                        listOfMachine.add(compactIndividu.get(idxMachineLast-1));
//                        tempCompactIndividu.add(compactIndividu.get(idxMachineLast-1));

                        //Isi ke retvalNext, isi ulang source dan destination
                        retvalNext.append(destination);
                        source = destination;
                        idxMachineLast++;
                        destination = retvalBef.charAt(idxMachineLast);
                    }
                    
//                    System.out.println("CASE 2");
//                    System.out.println("DEBUG : " + retvalNext);
//                    System.out.println("fund : " + fundUsed);
//                    System.out.println("hour : " + hourFactoryUsed);
//                    System.out.println("rent : " + nRentMachineUsed);
//                    System.out.println("timetravel : "+ minutesUsed);
//                    System.out.println("capacity : " + capacityUsed);
                    
                }else if (isTimeSufficient && !isCapacitySufficient){
                    //Jika waktu cukup,tapi kapasitas tidak, maka pulang ke pabrik
                    //Kembali ke P
                    hourFactoryUsed += this.pathList[source]['P'].length;

                    //RESET nilai constraint temporary
                    minutesUsed=0;
                    capacityUsed=0;

                    //Isi ke compactIndividu, compactIndividu tidak diisi
                    //retvalNext diisi
                    retvalNext.append('P');

                    //Source jadi P,destination tetap
                    source= 'P';
                    //idxMachineLast tidak bertambah
                    
                    //Isi listOfMachine ke tempCompactIndividu
                    while (!listOfMachine.isEmpty()){
                        tempCompactIndividu.add(listOfMachine.get(0));
                        listOfMachine.remove(0);
                    }
                    
//                    System.out.println("CASE 3");
//                    System.out.println("DEBUG : " + retvalNext);
//                    System.out.println("fund : " + fundUsed);
//                    System.out.println("hour : " + hourFactoryUsed);
//                    System.out.println("rent : " + nRentMachineUsed);
//                    System.out.println("timetravel : "+ minutesUsed);
//                    System.out.println("capacity : " + capacityUsed);
                    
                }else if (!isTimeSufficient && !isCapacitySufficient){
                    //Jika waktu cukup,tapi kapasitas tidak, maka pulang ke pabrik
                    //Kembali ke P
                    hourFactoryUsed += this.pathList[source]['P'].length;

                    //RESET nilai constraint temporary
                    minutesUsed=0;
                    capacityUsed=0;

                    //Isi ke compactIndividu, compactIndividu tidak diisi
                    //retvalNext diisi
                    retvalNext.append('P');

                    //Source jadi P,destination tetap
                    source= 'P';
                    //idxMachineLast tidak bertambah
                    
                    //Isi listOfMachine ke tempCompactIndividu
                    while (!listOfMachine.isEmpty()){
                        tempCompactIndividu.add(listOfMachine.get(0));
                        listOfMachine.remove(0);
                    }
                    
//                    System.out.println("CASE 4");
//                    System.out.println("DEBUG : " + retvalNext);
//                    System.out.println("fund : " + fundUsed);
//                    System.out.println("hour : " + hourFactoryUsed);
//                    System.out.println("rent : " + nRentMachineUsed);
//                    System.out.println("timetravel : "+ minutesUsed);
//                    System.out.println("capacity : " + capacityUsed);
                }
                
            }else{
                //Kasus mesin idxMachineLast-1 dan vendor idxMachineLast tidak bisa dipakai
                idxMachineLast++;
                //ganti destination
                destination = retvalBef.charAt(idxMachineLast);
                if (idxMachineLast==retvalBef.length()-1){
                    
                }
//                System.out.println("CASE ELSE");
//                System.out.println("idx : " +idxMachineLast);
//                System.out.println("DEBUG : " + retvalNext);
//                System.out.println("fund : " + fundUsed);
//                System.out.println("hour : " + hourFactoryUsed);
//                System.out.println("rent : " + nRentMachineUsed);
//                System.out.println("timetravel : "+ minutesUsed);
//                System.out.println("capacity : " + capacityUsed);
                
            }
            
        }

        if (isHourSufficient && retvalNext.charAt(retvalNext.length()-1)!='P'){
    
            //Kembali ke P
            hourFactoryUsed += this.pathList[source]['P'].length;

            //RESET nilai constraint temporary
            minutesUsed=0;
            capacityUsed=0;

            //Isi ke compactIndividu, compactIndividu tidak diisi
            //retvalNext diisi
            retvalNext.append('P');

            //Source jadi P,destination tetap
            source= 'P';
            //idxMachineLast tidak bertambah

            //Isi listOfMachine ke tempCompactIndividu
            while (!listOfMachine.isEmpty()){
                tempCompactIndividu.add(listOfMachine.get(0));
                listOfMachine.remove(0);
            }
        }
        
        //Kembalikan 
        compactIndividu = tempCompactIndividu;
        
        //DEBUG
//        this.PerformArrayListByte(compactIndividu);
//        System.out.println("retvalNext : " + retvalNext);
        
//       if (retvalNext.charAt(retvalNext.length()-1)!='P'){
//           System.out.println("ATA");
//       }else{
//           System.out.println("ETA");
//       }
        return retvalNext;
    }
    
    
    /**
     * function for evaluate fitness function
     */
    public void evaluateAllFitness(){
//        System.out.println("Evaluate all fitness");
        
        int i,j;
        
        //Compaction layer
        for(i=0;i<populationSize;++i){
            
//            System.out.println("i = " + i );
            //inisialisasi semua array boolean menjadi false
            
            for(j=0;j<isEverInsertedToCompactIndividu.length;++j){
                isEverInsertedToCompactIndividu[j] = false;
            }
            
            //Compaction layer
            for(j=0;j<population[i].length;++j){
                if (!isEverInsertedToCompactIndividu[ population[i][j] ]){
                    compactIndividu.add(population[i][j]);
                    isEverInsertedToCompactIndividu[ population[i][j] ] = true;
                }
            }
            
            //DEBUG
//            System.out.println("COMPACT LAYER :");
//            PerformArrayListByte(compactIndividu);
            
            //Layer path-finding
            //DEBUG UNTUK ODD EVEN
//            this.countMinimumTimeOddEven();
            
            //DEBUG UNTUK PATH_FINDING
            
            
            //Path-finding layer dengan menggunakan array compactIndividu
            //Temukan kumpulan path dengan waktu time <= 1 jam
            //return value berupa String berupa path mulai dari Pabrik
            StringBuffer retvalNext = this.pathFindingLayer(this.countMinimumTimeOddEven());
            fitnessValue[i] = EvaluationLayer(i, retvalNext);
            if (fitnessElitism<fitnessValue[i]){
                fitnessElitism=fitnessValue[i];
                
                elitismString = population[i];
                
                compactIndividuElitism.clear();
                for(j=0;j<compactIndividu.size();++j){
                    compactIndividuElitism.add(compactIndividu.get(j));
                }
                
                pathTruckElitism = retvalNext;
            }
//            sumFitValue += fitnessValue[i];
//            System.out.println(totalFitnessValue);
            compactIndividu.clear();
//            return;
        }
        
        // count fitness value
        
//        for(i=0;i<populationSize;++i){
////            fitnessValue[i] /= sumFitValue;
//            fitnessValue[i] = (fitnessValue[i]/totalFitnessValue) * 100;
//            if (fitnessElitism < fitnessValue[i]){
//                
//            }
//            System.out.println(fitnessValue[i]);
//        }
        
        //count-fitness layer
//        return;
//        System.out.println("Evaluate all Fitness");
//        this.PerformAllFitness();
        
        //LOG untuk GA dan GUI
        
    }
    
    
    public static StringBuffer ToTime(int minute){
        StringBuffer retval = new StringBuffer();
        retval.append("Selasa, ");
        retval.append(minute/60);
        retval.append(" : ");
        retval.append(minute % 60);
        return retval;
    }
    
    public static StringBuffer PrintMachine(int id){
        StringBuffer retval = new StringBuffer();
        retval.append("Mesin ");
        retval.append(id+1);        
        return retval;
    }
    
    public  StringBuffer PrintProduct(int id){
        StringBuffer retval = new StringBuffer();
        retval.append("Produk ");
        retval.append(id+1);       
        retval.append(" : ");
        retval.append(machineList[id].productivity);
        retval.append(" buah ");
        return retval;
    }
    
    public long EvaluationLayer(int ith, StringBuffer pathTruck){
//        System.out.println(pathTruck);
        // return product * keuntungan       
        int currentMinute = 0;
        // cari P kedua, -> truk sudah pulang
        int currentIdxPath = 1;
        boolean pFound = false;
        char pointBefore = 'P';
        char pointNow = ' ';
        HashMap<Byte, Integer> kedatanganMesin = new HashMap<Byte, Integer>();

        ArrayList<Byte> listOfMesinDatangTemp = new ArrayList<Byte>();
        ArrayList<Byte> listOfMesinDatang = new ArrayList<Byte>();

        int factoryStart = 0;
        int nCompaq=0;
        while (currentIdxPath < pathTruck.length()) {
            pointNow = pathTruck.charAt(currentIdxPath);
            //debug
//            System.out.println("Perpindahan antar titik "+pointBefore + " , "+pointNow);
            int distance = pathList[(int) pointBefore][(int) pointNow].length;
            currentMinute += distance;
            
            if (pointNow == 'P') {
                for (int i = 0; i < listOfMesinDatangTemp.size(); i++) {
//                    System.out.println("Put " + (listOfMesinDatangTemp.get(i))+" "+currentMinute);
                    kedatanganMesin.put(listOfMesinDatangTemp.get(i), currentMinute);
                }
                if (!pFound) {
                    pFound = true;
                    factoryStart = currentMinute;
                }
                listOfMesinDatangTemp.clear();
            } else {
                if (nCompaq< compactIndividu.size()){
                    byte b = compactIndividu.get(nCompaq);
                    listOfMesinDatangTemp.add(b);
                    nCompaq++;    
                }
            }
            currentIdxPath++;
            pointBefore = pointNow;
        }

//        System.out.println(kedatanganMesin);
//        System.out.println(listOfMesinDatang);
//        System.out.println(listOfMesinDatangTemp);
        int idxMachine = 0;
        long totalProfit = 0;
        byte currentIdMachine;
        currentMinute = factoryStart;// reset current minute ke waktu pabrik mulai operasi
        while (idxMachine < population[ith].length && (currentMinute + 60) <= factory.hour * 60) {
            currentIdMachine = population[ith][idxMachine];          
//            System.out.println("id "+currentIdMachine + " idx " +idxMachine);
            Machine currentMachine = machineList[currentIdMachine];
            Integer kedatangan = kedatanganMesin.get(new Byte(currentIdMachine));
//            System.out.println(kedatangan + " "+kedatanganMesin);
            if (kedatangan != null) {
                if (kedatangan <= currentMinute) {
                    Set<Byte> keySet = kedatanganMesin.keySet();
                    for (Byte b: keySet){
                        if (!listOfMesinDatang.contains(b)) {
                            listOfMesinDatang.add(b);
                        }
                    }
                    
                    if (currentMachine.idDependency >=0 ) {                                               
                        // punya depedensi                        
                        Machine dependMachine = machineList[currentMachine.idDependency];
                        if (!currentMachine.isDead()) {
                            if (dependMachine.UseProduct(currentMachine.totalDependency)) {
                                if (currentMachine.ProducePerHour()) {
//                                    System.out.println(currentMachine .idMachine+" Berhasil menggunakan produk dari " + dependMachine.idMachine + "dan meproduksi " + currentMachine.productivity);
                                    currentMinute += 60;
//                                    System.out.println("Current "+currentMachine);
                                    for (int i = 0; i < listOfMesinDatang.size(); i++) {                                                      
                                        if (i != currentIdMachine) {// mesin selain current mesin istirahat
                                            Machine otherMachine = machineList[i];
                                            otherMachine.needRest = true;
                                            otherMachine.Rest();                       
//                                            System.out.println(otherMachine);
                                        }
                                    }
                                }
                            }else{
//                                System.out.println(currentMachine .idMachine + " ADA DEPEDENCY tapi ga bisa produksi");
                            }
                        }                     
                    } else {
                        if (currentMachine.ProducePerHour()) {
                            // current mesin beroperasi
//                            System.out.println("mesin " + currentMachine.idMachine + " produksi " + currentMachine.productivity);
                            currentMinute += 60;
//                            System.out.println("Current "+currentMachine);
                            for (int i = 0; i < listOfMesinDatang.size(); i++) {                                                      
                                if (i != currentIdMachine) {// mesin selain current mesin istirahat
                                    Machine otherMachine = machineList[i];
                                    otherMachine.needRest = true;
                                    otherMachine.Rest();                       
//                                    System.out.println(otherMachine);
                                }
                            }
                            
                        } else {
                            if (currentMachine.isDead()) {
//                                System.out.println("mesin " + currentMachine.idMachine + " sudah mati");
                            }
                        }
                    }
                   
                    idxMachine++;
                } else {
                    // delay hingga mesin datang
                    currentMinute = kedatanganMesin.get(currentIdMachine);
                }
            } else {
//                System.out.println("Ga sanggup membeli mesin " + (population[ith][idxMachine] ));                
                idxMachine++;
            }
        }

        for (Byte i : listOfMesinDatang) {
            totalProfit += machineList[i].TotalProfit()-(long)(machineList[i].rentCost);
        }

        for (Machine m : machineList) {
            m.ResetMachine();
        }
        return totalProfit;
    }
    
    public String EvaluationIndividu(byte [] population, StringBuffer pathTruck, ArrayList<Byte> compactIndividu){
        StringBuilder sb  = new StringBuilder();
        int currentMinute = 0;
        // cari P kedua, -> truk sudah pulang
        int currentIdxPath = 1;
        boolean pFound = false;
        char pointBefore = 'P';
        char pointNow = ' ';
        HashMap<Byte, Integer> kedatanganMesin = new HashMap<Byte, Integer>();

        ArrayList<Byte> listOfMesinDatangTemp = new ArrayList<Byte>();
        ArrayList<Byte> listOfMesinDatang = new ArrayList<Byte>();

        long modal = 0;
        sb.append("Statistik \n");
        sb.append("Uang Awal $ "+factory.fund);
        sb.append('\n');
        sb.append('\n');
        sb.append("Daftar Mesin yang disewa \n");
        for (Byte b : compactIndividuElitism){
            sb.append(PrintMachine(b));
            sb.append("\t : $ ");
            sb.append(machineList[b].rentCost);
            modal += machineList[b].rentCost;
            sb.append('\n');
        }        
        sb.append('\n');        
        sb.append("Total modal = $ ");
        sb.append(modal);
        sb.append('\n');           
        sb.append('\n');
                
        int factoryStart = 0;
        int nCompaq=0;
        while (currentIdxPath < pathTruck.length()) {
            pointNow = pathTruck.charAt(currentIdxPath);
            //debug
//            System.out.println("Perpindahan antar titik "+pointBefore + " , "+pointNow);
            int distance = pathList[(int) pointBefore][(int) pointNow].length;
            currentMinute += distance;
            
            if (pointNow == 'P') {
                for (int i = 0; i < listOfMesinDatangTemp.size(); i++) {
//                    System.out.println("Put " + (listOfMesinDatangTemp.get(i))+" "+currentMinute);
                    kedatanganMesin.put(listOfMesinDatangTemp.get(i), currentMinute);
                }
                if (!pFound) {
                    pFound = true;
                    factoryStart = currentMinute;
                }
                listOfMesinDatangTemp.clear();
            } else {
                if (nCompaq< compactIndividu.size()){
                    byte b = compactIndividu.get(nCompaq);
                    listOfMesinDatangTemp.add(b);
                    nCompaq++;    
                }
            }
            currentIdxPath++;
            pointBefore = pointNow;
        }

//        System.out.println(kedatanganMesin);
//        System.out.println(listOfMesinDatang);
//        System.out.println(listOfMesinDatangTemp);
        int idxMachine = 0;
        long totalProfit = 0;
        byte currentIdMachine;
        currentMinute = factoryStart;// reset current minute ke waktu pabrik mulai operasi
        while (idxMachine < population.length && (currentMinute + 60) <= factory.hour * 60) {
            currentIdMachine = population[idxMachine];          
//            System.out.println("id "+currentIdMachine + " idx " +idxMachine);
            Machine currentMachine = machineList[currentIdMachine];
            Integer kedatangan = kedatanganMesin.get(new Byte(currentIdMachine));
//            System.out.println(kedatangan + " "+kedatanganMesin);
            if (kedatangan != null) {
                if (kedatangan <= currentMinute) {
                    Set<Byte> keySet = kedatanganMesin.keySet();
                    for (Byte b: keySet){
                        if (!listOfMesinDatang.contains(b)) {
                            listOfMesinDatang.add(b);
                            sb.append(ToTime(kedatanganMesin.get(b) )).append('\n');
                            sb.append(PrintMachine(b)).append(" datang ").append('\n') ;                        
                        }
                    }
                    
                    if (currentMachine.idDependency >=0 ) {                                               
                        // punya depedensi                        
                        Machine dependMachine = machineList[currentMachine.idDependency];
                        if (!currentMachine.isDead()) {
                            if (dependMachine.UseProduct(currentMachine.totalDependency)) {
                                if (currentMachine.ProducePerHour()) {
//                                    System.out.println(currentMachine .idMachine+" Berhasil menggunakan produk dari " + dependMachine.idMachine + "dan meproduksi " + currentMachine.productivity);
                                    sb.append(ToTime(currentMinute)).append('\n');
                                    sb.append(PrintMachine(currentIdMachine)).append(" menggunakan produk ").append(PrintMachine(dependMachine.idMachine))
                                            .append(" akan menghasilkan ").append(PrintProduct(currentIdMachine).append('\n'));                                                            
 
                                    currentMinute += 60;                                    
//                                    System.out.println("Current "+currentMachine);
                                    for (int i = 0; i < listOfMesinDatang.size(); i++) {                                                      
                                        if (i != currentIdMachine) {// mesin selain current mesin istirahat
                                            Machine otherMachine = machineList[i];
                                            otherMachine.needRest = true;
                                            otherMachine.Rest();                       
//                                            System.out.println(otherMachine);
                                        }
                                    }
                                }
                            }else{
//                                System.out.println(currentMachine .idMachine + " ADA DEPEDENCY tapi ga bisa produksi");
                            }
                        }                     
                    } else {
                        if (currentMachine.ProducePerHour()) {
                            // current mesin beroperasi
//                            System.out.println("mesin " + currentMachine.idMachine + " produksi " + currentMachine.productivity);
                            sb.append(ToTime(currentMinute)).append('\n');
                            sb.append(PrintMachine(currentIdMachine)).append(" akan menghasilkan ").append(PrintProduct(currentIdMachine)).append('\n');                                                            
                            currentMinute += 60;
//                            System.out.println("Current "+currentMachine);
                            for (int i = 0; i < listOfMesinDatang.size(); i++) {                                                      
                                if (i != currentIdMachine) {// mesin selain current mesin istirahat
                                    Machine otherMachine = machineList[i];
                                    otherMachine.needRest = true;
                                    otherMachine.Rest();                       
//                                    System.out.println(otherMachine);
                                }
                            }
                            
                        } else {
                            if (currentMachine.isDead()) {
//                                System.out.println("mesin " + currentMachine.idMachine + " sudah mati");
                            }
                        }
                    }
                   
                    idxMachine++;
                } else {
                    // delay hingga mesin datang
                    sb.append(ToTime(currentMinute)).append('\n');
                    sb.append(PrintMachine(currentIdMachine)).append(" belum tiba di pabrik\n");                                                                                
                    currentMinute = kedatanganMesin.get(currentIdMachine);
                }
            } else {
//                System.out.println("Ga sanggup membeli mesin " + (population[ith][idxMachine] ));                
                idxMachine++;
            }
        }
       sb.append(ToTime(currentMinute)).append('\n');
        sb.append("Pabrik selesai beroperasi\n");
        sb.append('\n');
        sb.append("Hasil Produksi Pabrik Hari Ini \n");        
 
        long totalLaba=0;
        for (Byte i : listOfMesinDatang) {
            Machine m =  machineList[i];
                sb.append(PrintMachine(i));
            sb.append("\t : ");
            sb.append(m.totalProduct);
            sb.append('\t');
            sb.append("x $ ");                    
            sb.append(m.profitProduct);            
            sb.append(" = $ ");            
            long total = m.TotalProfit();
            long laba = total  -(long)(m.rentCost);
            sb.append(total);            
            sb.append('\n');            
            totalProfit += total;
            totalLaba += laba;
        }
               sb.append('\n');           
        sb.append("Total pemasukkan = $ ");
        sb.append(totalProfit);
        sb.append('\n');           
        sb.append('\n');
        
        sb.append("Profit           = $ ");
        sb.append(totalLaba);
        sb.append('\n');   
 

        for (Machine m : machineList) {
            m.ResetMachine();
        }
        return sb.toString();
    }
    
    /**
     * Selection process, use Roulette Wheel and Steady state, and
     */
    public void Selection(){
//        System.out.println("Selection");
//        SelectionWithRoulette();
        SelectionWithSteadyState();
//        System.out.println("Selection");
//        PerformAllFitness();
    }
    
    /**
     * Selection with roulette wheel and Elitism is saved at index=0
     * GUARANTEE ELITISM
     */
    public void SelectionWithRoulette(){
        //S is the sum of fitness
        float []incrementFitnessValue = new float[fitnessValue.length];
        int i,j,temp,idxMaxFitness,S,r;
        
        //Count the sum of all chromosome and elitism
        incrementFitnessValue[0] = fitnessValue[0];
        S=(int)fitnessValue[0];
        idxMaxFitness = 0;
        for(i=1;i<fitnessValue.length;++i){
            incrementFitnessValue[i] = fitnessValue[i] + incrementFitnessValue[i-1];
            S += fitnessValue[i];
            if (fitnessValue[idxMaxFitness]<fitnessValue[i]){
                idxMaxFitness = i;
            }
            
        }
        
        //Setting the elitism at index 0
        fitnessValueTemp[0] = fitnessValue[idxMaxFitness];
        //Setting the tempPopulation
        tempPopulation[0] = population[idxMaxFitness];
        
        //Start the selection
        for(i=1;i<idxMaxFitness;++i){
            //select a random number r in interval [0,S]
            r = randomGA.nextInt(S+1);
            
            //search the index of least totalvalue bigger than r using incrementFitnessValue
            if (r<incrementFitnessValue[incrementFitnessValue.length/2]){
                for(j=0;j<=incrementFitnessValue.length/2;++j){
                    if (r<incrementFitnessValue[j]){
                        //Found the selected roulette
                        //change the array of fitnessValue
                        fitnessValueTemp[i] = fitnessValue[j];
                        //change the array of population
                        tempPopulation[i] = population[j];
                    }
                }
            }else{
                for(j=incrementFitnessValue.length/2 + 1;j<incrementFitnessValue.length;++j){
                    if (r<incrementFitnessValue[j]){
                        //Found the selected roulette
                        //change the array of fitnessValue
                        fitnessValueTemp[i] = fitnessValue[j];
                        //change the array of population
                        tempPopulation[i] = population[j];
                    }
                }
            }
        }
        
        for(i=idxMaxFitness+1;i<fitnessValueTemp.length;++i){
            //select a random number r in interval [0,S]
            r = randomGA.nextInt(S+1);
            
            //search the index of least totalvalue bigger than r using incrementFitnessValue
            if (r<incrementFitnessValue[incrementFitnessValue.length/2]){
                for(j=0;j<=incrementFitnessValue.length/2;++j){
                    if (r<incrementFitnessValue[j]){
                        //Found the selected roulette
                        //change the array of fitnessValue
                        fitnessValueTemp[i] = fitnessValue[j];
                        //change the array of population
                        tempPopulation[i] = population[j];
                    }
                }
            }else{
                for(j=incrementFitnessValue.length/2 + 1;j<incrementFitnessValue.length;++j){
                    if (r<incrementFitnessValue[j]){
                        //Found the selected roulette
                        //change the array of fitnessValue
                        fitnessValueTemp[i] = fitnessValue[j];
                        //change the array of population
                        tempPopulation[i] = population[j];
                    }
                }
            }
        }
        
        //replace the new fitness function value
        fitnessValue = fitnessValueTemp;
        tempPopulation = population;
    }
    
    
    /*
     * Sorting descendant with bubble, with key is the fitness, startpoint to endpoint-1
     */
    public void BubbleSortFitnessAndPopulation(int startpoint,int endpoint){
        int i,j;
//        System.out.println("i,j : " + i + "," + j);
        long temp;
        for(i=startpoint;i<endpoint-1;++i){
            for(j=i+1;j<endpoint;++j){
//                System.out.println("i,j : " + i + "," + j);
                if (fitnessValue[i]<fitnessValue[j]){
                    //Swap fitnessValue and population
                    temp = fitnessValue[i];
                    fitnessValue[i] = fitnessValue[j];
                    fitnessValue[j] = temp;
                    
                    individu = population[i];
                    population[i] = population[j];
                    population[j] = individu;
                }
            }
        }
    }
    
    /**
     * Selection with steady state technique
     * GUARANTEE ELITISM
     */
    public void SelectionWithSteadyState(){
        //fitnessValueTemp is for sorted fitnessValue
        
        int i,j;
        
        
//        BubbleSortFitnessAndPopulation(0,fitnessValue.length);
        
        //Copy fitnessValueTemp
        for(i=0;i<fitnessValueTemp.length;++i){
            fitnessValueTemp[i] = fitnessValue[i];
        }
        //Copy tempPopulation
        for(i=0;i<population.length;++i){
            tempPopulation[i]=population[i];
        }
        
//        System.out.println("Habis DiSORT");
//        this.PerformAllFitness();
        
        //buang yang buruk, elitism tetap dipertahankan, yang terbaik ikut diambil
        for(i=fitnessValueTemp.length-1;i>=fitnessValueTemp.length-thresold;--i){
            fitnessValueTemp[i] = fitnessValueTemp[fitnessValueTemp.length-1-i];
            tempPopulation[i] = tempPopulation[fitnessValueTemp.length-1-i];
        }
        
        //Change the new value of fitness and population
        fitnessValue = fitnessValueTemp;
        population = tempPopulation;
    }
    
    /**
     * 
     */
    public void CrossOver(){
        CrossOverWithSinglePointBinary();
//        System.out.println("CrossOver");
//        this.PerformAllFitness();
    }
    
    /**
     * Cross over with single point in the middle
     * GUARANTEE ELITSM
     */
    public void CrossOverWithSinglePointBinary(){
        int firstidx,secondidx,i,idxBinary = chromosomeLength/2;
        byte r;
        
        //Sorting descendant fitness and population 
        //JANGAN sorting, akan mengurangi peluang untuk bisa berkembang
//        BubbleSortFitnessAndPopulation(1,populationSize);
        
        //CrossOver dari index 0 sampai length-1;
        firstidx=0;
        secondidx=1;
        
        while (secondidx<populationSize){
            //tentukan random value untuk crossover probability
            r = (byte)(randomGA.nextInt(100));
            
            //gunakan probabilitas untuk menentukan apakah diperlukan cross-over
            if (r<crossOverProbability){
                //DEBUG
//                System.out.println("kena crossover, population i,j : " +firstidx + "," + secondidx);
                
//                Crossover 2 consecutive individu index i dan j
                for(i=0;i<=idxBinary;++i){
                    individu[i] = population[firstidx][i];
                    individu2[i] = population[secondidx][i];
                }
                for(i=idxBinary+1;i<chromosomeLength;++i){
                    individu[i] = population[secondidx][i];
                    individu2[i] = population[firstidx][i];
                }
                
//                population[firstidx] = individu;
//                population[secondidx] = individu2;
                
                int j;
                for(j=0;j<individu.length;++j){
                    population[firstidx][j]=individu[j];
                    population[secondidx][j]=individu2[j];
                }
                
//                DEBUG
//                System.out.print("population " + firstidx + ", :");this.PerformArr(population[firstidx]);
//                System.out.print("population " + secondidx + ", :");this.PerformArr(population[secondidx]);
//                System.out.print("individu 1, :");this.PerformArr(individu);
//                System.out.print("individu 2, :");this.PerformArr(individu2);
//                System.out.println("POPULATION :");
//                this.PerformAllFitness();
            }
            
            //tambah nilai firstidx dan secondidx
            firstidx+=2;
            secondidx+=2;
        }
        
//        System.out.println("CrossOver BEFORE");
//        this.PerformAllFitness();
    }
    
    
    /**
     * Mutation process used according
     */
    public void Mutation(){
        MutationWithIntegerInversion();
        
        //DEBUG
//        System.out.println("Mutation");
//        this.PerformAllFitness();
    }
    
    /**
     * Mutation process using integer inversion
     */
    public void MutationWithIntegerInversion(){
        int r,i,j;
        for(i=0;i<population.length;++i){
            for(j=0;j<population[i].length;++j){
                r = randomGA.nextInt(10000);
                if (r<mutationProbability){
                    //lakukan mutasi dengan swap ke element yang baru
                    population[i][j] = (byte)randomGA.nextInt(this.M);
                    
                    //DEBUG
//                    System.out.println("lakukan mutasi pada i,j : " + i + "," + j + "menjadi " + population[i][j]);
                }
            }
        }
    }
    
    
    public void PerformArr2D(byte [][]arr2D){
        int i,j;
        for(i=0;i<arr2D.length;++i){
            for(j=0;j<arr2D[i].length;++j){
                System.out.print(arr2D[i][j] + " ");
            }
            System.out.println();
        }
    }
    
    public void PerformArr(byte []arr1D){
        int i;
        for(i=0;i<arr1D.length;++i){
            System.out.print(arr1D[i] + " ");
        }
        System.out.println();
    }
    
    public boolean isStop = false;
    
    public void setStop(){
        isStop = true;
    }
    
    public int counterLog=4;
    public boolean demoMode=true;
    public void setDemoMode(){
        this.demoMode = true;
    }
    public void setBankaiMode(){
        this.demoMode = false;
    }
    
    public StringBuffer solveSolution(){
        int i,j;
        StringBuffer ret = new StringBuffer();
        
        this.GeneratePopulate();
        //LOG untuk GA dan GUI
        LogForGA.append("Generate populasi dengan ukuran populasi=" + populationSize + ", and panjang tiap kromosom="+chromosomeLength+"\n");
        for(i=0;i<populationSize;++i){
            for(j=0;j<population[i].length;++j){
                LogForGA.append(population[i][j] + " ");
            }
            AABLog.add(new ArrayList<Byte>(ABLog));
            LogForGA.append("\n");
        }
        LogForGA.append("\n");
        
//        this.PerformArr2D(population);
        int counter = 0;
        while (!isStop && counter <100){
//            System.out.println("Populasi");
//            this.PerformArr2D(population);
            //Evaluate fitness function
//            System.out.println("counter : " + counter);
            
            if (demoMode && counter<counterLog){
                LogForGA.append("Loop ke - "+counter+"\n");
            }
            
            this.evaluateAllFitness();
            
            if (demoMode && counter<counterLog){
                LogForGA.append("Tahap evaluasi Fitness Function\n");
                LogForGA.append("index : nilai fitness , kromosom  ");
                ABLog.clear();
                for(i=0;i<this.population.length;++i){
                    LogForGA.append(i + " : " + this.fitnessValue[i] + ",kromosom : ");
                    for(j=0;j<this.population[i].length;++j){
                        LogForGA.append(population[i][j] + " ");
                        ABLog.add(population[i][j]);
                    }
                    AABLog.add(new ArrayList<Byte>(ABLog));
                    ABLog.clear();
                    LogForGA.append("\n");
                }
                LogForGA.append("\n");
                
                
            }
            
            //Selection the population
            this.Selection();
            
            if (demoMode && counter<counterLog){
                LogForGA.append("Tahap Selection dilakukan dengan steady-state\n");
                ABLog.clear();
                for(i=0;i<population.length;++i){
                    if (i==population.length-1){
                        LogForGA.append("individu ke -" + i + " diganti dengan individu terbaik,yaitu ");
                    }else{
                        LogForGA.append("individu ke -" + i + " : ");
                    }
                    for(j=0;j<population[i].length;++j){
                        LogForGA.append(population[i][j] + " ");
                        ABLog.add(population[i][j]);
                    }
                    LogForGA.append("\n");
                    AABLog.add(new ArrayList<Byte>(ABLog));
                    ABLog.clear();
                }
                LogForGA.append("\n");
            }
//            
            //Crossover the parent
            this.CrossOver();/// tono : array out of bound exception
            if (demoMode && counter<counterLog){
                LogForGA.append("Tahap Cross over dengan menggunakan single point in middle dengan probability "+crossOverProbability + "%\n");
                LogForGA.append("Perhatikan bahwa individu ke i dikawinkan dengan individu ke i+1 menjadi individu baru di posisi i dan i+1(dimulai dari i=0)\n");
                ABLog.clear();
                for(i=0;i<population.length;++i){
                    LogForGA.append("individu baru ke - " + i + " :");
                    for(j=0;j<population[i].length;++j){
                        LogForGA.append(population[i][j] + " ");
                        ABLog.add(population[i][j]);
                    }
                    AABLog.add(new ArrayList<Byte>(ABLog));
                    ABLog.clear();
                    LogForGA.append("\n");
                }
                LogForGA.append("\n");
            }
            
//            
//            //Mutation the population
            this.Mutation();
            if (demoMode && counter<counterLog){
                LogForGA.append("Tahap Mutation dengan probability "+(float)mutationProbability/100.0 + "%\n");
                ABLog.clear();
                for(i=0;i<population.length;++i){
                    LogForGA.append("individu ke - "+ i + " dimutasi menjadi ");
                    for(j=0;j<population[i].length;++j){
                        LogForGA.append(population[i][j] + " ");
                        ABLog.add(population[i][j]);
                    }
                    AABLog.add(new ArrayList<Byte>(ABLog));
                    ABLog.clear();
                    LogForGA.append("\n");
                }
                LogForGA.append("\n");
            }
            counter++;
//            return;
        }
        //Lempar solusi maximum ke world
//        this.PerformArr2D(population);
        if (pathTruckElitism!=null){
        // DEBUG
//        System.out.println("Solution");
//        System.out.print("Individu ");
//        this.PerformArr(elitismString);
//        System.out.println("Compact "+compactIndividuElitism);
//        System.out.println("Path "+pathTruckElitism);
//        System.out.println("Profit="+fitnessElitism);
        ret.append( EvaluationIndividu(elitismString, pathTruckElitism, compactIndividuElitism));        
        ret.append('\n');        
        }else{
            ret.append("No Solution\n");
        }
        return ret;
    }
    
    public void PerformArrayListByte(ArrayList<Byte> arb){
        int i;
        for(i=0;i<arb.size();++i){
            System.out.print(arb.get(i) + " ");
        }
        System.out.println();
    }
    
    public static void main(String[] args){
        int a=1000000,b=1000000;
        long c=(long)a*(long)b;
//        System.out.println(c);
        
       
    }
}
